package com.fjwt.gz.coupon.lkls;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fjwt.gz.core.exception.BizException;
import com.fjwt.gz.coupon.SendCouponBo;
import com.fjwt.gz.coupon.lkls.lklspayutil.HttpRequest;
import com.fjwt.gz.coupon.lkls.lklspayutil.RSAUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 拉卡拉发券
 */
@Slf4j
@Service
public class LklspayCouponSendService {
    //TODO 注意： 上面记得封装一层，统一调用，通过不同通道调用不同的逻辑，
    private static final Logger lklsPayLogger = LoggerFactory.getLogger("lklsPay");

    public JSONObject couponSend(SendCouponBo sendCouponBo) {
        String logPrefix = "【拉卡拉支付通道发券】";
        lklsPayLogger.info("{},进来的参数信息是：{}", JSONUtil.toJsonStr(sendCouponBo));

        if (Objects.isNull(sendCouponBo)) {
            throw new BizException("发券参数缺失！");
        }
        try {
            JSONObject lklAuthConfig = sendCouponBo.getAuthConfig();
            lklsPayLogger.info("{},配置参数：{}", logPrefix, JSONUtil.toJsonStr(lklAuthConfig));
            // 公共参数封装
            Map<String, String> pubMap = extracted(lklAuthConfig);
            lklsPayLogger.info("{},公共参数封装后信息：{}", logPrefix, JSONUtil.toJsonStr(pubMap));

            //业务参数封装
            ImmutableMap<String, String> map = lklsPost(sendCouponBo, lklAuthConfig.getString("notifyUrl"));
            HashMap<String, String> hashMap = Maps.newHashMap(map);
            lklsPayLogger.info("{},业务参数封装后信息：{}", logPrefix, JSONUtil.toJsonStr(hashMap));

            // 1、对业务参数进行URLEncoder转码（UTF-8），否则将出现中文乱码问题
            String bizContentJsonStr = JSON.toJSONString(hashMap);
            lklsPayLogger.info("{},bizContent原生参数：{}", logPrefix, bizContentJsonStr);
            String bizContent = java.net.URLEncoder.encode(JSON.toJSONString(hashMap), "UTF-8");
            lklsPayLogger.info("{},bizContent转码后：{}", logPrefix, bizContent);

            // 2、对业务参数进行RSA加密
            String biz_content = RSAUtils.encryptByPrivateKey(bizContent, lklAuthConfig.getString("privateKeyStr"));
            pubMap.put("bizContent", biz_content);
            lklsPayLogger.info("bizContent加密后：{}", biz_content);

            // 确认业务参数正常加解密是否正确
            String biz_content2 = URLDecoder.decode(RSAUtils.decryptByPublicKey(biz_content, lklAuthConfig.getString("publicKeyStr")), "UTF-8");
            lklsPayLogger.info("bizContent解密后：{}", biz_content2);

            // 3、对请求参数进行RSA私钥签名
            String signContent = RSAUtils.getEncodeSignContent(pubMap);
            lklsPayLogger.info("签名原生内容：{}", signContent);
            String sign = RSAUtils.signByPrivateKey(signContent.getBytes(), lklAuthConfig.getString("privateKeyStr"));

            lklsPayLogger.info("接口参数RSA签名：{}", sign);
            pubMap.put("sign", sign);

            // 确认签名是否正确（公钥验签）
            boolean a = RSAUtils.verifyByPublicKey(signContent.getBytes(), lklAuthConfig.getString("publicKeyStr"), sign);
            lklsPayLogger.info("验签结果：{}", a);

            // 4、接口调用
            String res = HttpRequest.sendPost(lklAuthConfig.getString("sendCouponUrl"), pubMap);
            lklsPayLogger.info("响应数据res：{}", res);
            JSONObject jsonObject = JSONObject.parseObject(res);

            //5、使用平台公钥对响应数据（data） 进行解密
            String responseData = RSAUtils.decryptByPublicKey(URLDecoder.decode(jsonObject.get("data").toString(), "UTF-8"), lklAuthConfig.getString("supplierPublicKeyStr"));
            lklsPayLogger.info("接口处理结果：{}", responseData);

            if (responseData.isEmpty()) {
                lklsPayLogger.info("{} 请求失败,返回内容为空，请求参数：{}", logPrefix, JSONUtil.toJsonStr(responseData));
                throw new BizException("发券失败");
            }
            JSONObject respData = JSONObject.parseObject(responseData);
            if (!"000000".equals(respData.getString("resCode")) || "FAIL".equals(respData.getString("tradeStatus"))) {
                lklsPayLogger.info("{} 请求失败，请求参数：{},返回参数：{}", logPrefix, JSONUtil.toJsonStr(responseData), JSONUtil.toJsonStr(respData));
                throw new BizException("发券失败");
            }
            lklsPayLogger.info("{},发券接口返回的数据是：{}", logPrefix, JSONUtil.toJsonStr(respData));
            return respData;
        } catch (BizException e) {
            lklsPayLogger.info("拉卡拉发券失败");
            throw e;
        } catch (Exception e) {
            lklsPayLogger.info("拉卡拉发券失败");
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 公共参数
     *
     * @param lklAuthConfig
     * @return
     */
    private Map<String, String> extracted(JSONObject lklAuthConfig) {
        Map<String, String> pubMap = new HashMap<>();
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String timestamp = dateFormat.format(date);
        pubMap.put("orgid", lklAuthConfig.getString("orgid"));// 分配接入方的标识id
        pubMap.put("method", "postVorcher");// 接口名称
        pubMap.put("format", "JSON");// 数据格式: JSON
        pubMap.put("charset", "UTF-8");// 编码格式: UTF-8
        pubMap.put("signType", "RSA2");// 算法类型: RSA2
        pubMap.put("timestamp", timestamp);// 发送请求时间
        pubMap.put("version", "1.0");// 接口版本号
        return pubMap;
    }

    /**
     * 业务参数
     *
     * @return
     */
    private ImmutableMap<String, String> lklsPost(SendCouponBo sendCouponBo, String notifyUrl) {
        String callTradeNo = System.currentTimeMillis() + RandomUtil.randomNumbers(6);
        // 业务参数（bizContent）
        ImmutableMap.Builder<String, String> bizBuilder = ImmutableMap.builder();
        ImmutableMap<String, String> map = bizBuilder
                .put("openId", sendCouponBo.getOpenId())
                .put("batchCode", sendCouponBo.getStockId())// 活动编码{"stockId": 银盛批次号}
                .put("callTradeNo", callTradeNo)// 交易流水号
                .put("writeOffNotify", notifyUrl)// 核销事件通知地址
                .build();
        return map;
    }

}
